#ifndef __GLES2FBORTT_H__
#define __GLES2FBORTT_H__

#include "GLES2RenderTexture.h"
#include "GLES2Context.h"
#include "GLES2FrameBufferObject.h"

namespace PVM
{
	class GLES2FBOManager;
	class GLES2RenderBuffer;
	
	class GLES2FBORenderTexture : public GLES2RenderTexture
	{
	public:
		GLES2FBORenderTexture(GLES2FBOManager* manager, const std::string& name, const GLES2SurfaceDesc& target, bool writeGamma, uint fsaa);
		virtual void getCustomAttribute(const std::string& name, void* pData);
		virtual void swapBuffers(bool waitForVSync = true);
		virtual bool attachDepthBuffer(DepthBuffer* depthBuffer);
		virtual void detachDepthBuffer();
		virtual void _detachDepthBuffer();
	protected:
		GLES2FrameBufferObject mFB;
	};
	
	class GLES2FBOManager : public GLES2RTTManager
	{
	public:
		GLES2FBOManager();
		~GLES2FBOManager();
		
		void bind(RenderTarget* target);
		void unbind(RenderTarget* target) {};
		void getBestDepthStencil(GLenum internalFormat, GLenum* depthFormat, GLenum* stencilFormat);
		virtual GLES2FBORenderTexture* createRenderTexture(const std::string& name,
							const GLES2SurfaceDesc& target, bool writeGamma, uint fsaa);
		virtual MultiRenderTarget* createMultiRenderTarget(const std::string& name);
		GLES2SurfaceDesc requestRenderBuffer(GLenum format, size_t width, size_t height, uint fsaa);
		void requestRenderBuffer(const GLES2SurfaceDesc& surface);
		void releaseRenderBuffer(const GLES2SurfaceDesc& surface);
		bool checkFormat(PixelFormat format) {return mProps[format].valid;}
		GLuint getTemporaryFBO() {return mTempFBO;}
	private:
		struct FormatProperties
		{
			bool valid;
			struct Mode
			{
				size_t depth;
				size_t stencil;
			};
			
			vector<Mode>::type modes;
		};
		
		FormatProperties mProps[PF_COUNT];
		
		struct RBFormat
		{
			RBFormat(GLenum format, size_t width, size_t height, uint fsaa) :
			format(format), width(width), height(height), samples(fsaa)
			{}
			GLenum format;
			size_t width;
			size_t height;
			uint samples;
			
			bool operator < (const RBFormat& other) const
			{
				if (format < other.format)
				{
					return true;
				}
				else if (format == other.format)
				{
					if (width < other.width)
					{
						return true;
					}
					else if (width == other.width)
					{
						if (height < other.height)
							return true;
						else if (height == other.height)
						{
							if (samples < other.samples)
								return true;
						}
					}
				}
				return false;
			}
		};
		struct RBRef
		{
			RBRef() {}
			RBRef(GLES2RenderBuffer* buffer) :
			buffer(buffer), refcount(1)
			{}
			GLES2RenderBuffer* buffer;
			size_t refCount;
		};
		typedef map<RBFormat, RBRef>::type RenderBufferMap;
		RenderBufferMap mRenderBufferMap;
		
		GLuint mTempFBO;
		
		void detectFBOFormats();
		GLuint _tryFormat(GLenum depthFormat, GLenum stencilFormat);
		bool _tryPackedFormat(GLenum packedFormat);
	};
}

#endif
